wayland: Make subsurface desynchronized after first parent surface frame
authorJonas Ådahl <jadahl@gmail.com>
Thu, 10 Sep 2015 09:42:21 +0000 (17:42 +0800)
committerJonas Ådahl <jadahl@gmail.com>
Tue, 15 Sep 2015 00:11:47 +0000 (08:11 +0800)
Initially the subsurface will be in synchronized mode and we will leave
it like this until the first time the parent surface has been committed.
The reason for this is because the subsurface position will be applied
as part of the parent surface state, and we need to synchronize the
initial position with the initial frame, so that we don't accidentally
draw the subsurface at the default position (0, 0) which would happen in
desynchronized mode if the subsurface content is committed before the
next parent surface commit.

https://bugzilla.gnome.org/show_bug.cgi?id=754839

gdk/wayland/gdkwindow-wayland.c

index 4c6f30ce3516ad76dba2075a7986a2cb44a9deaa..ab7f384b8e79641dfe9527ab3143a3428bf2a439 100644 (file)
 #include <string.h>
 #include <errno.h>
 
+enum {
+  COMMITTED,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
 #define WINDOW_IS_TOPLEVEL_OR_FOREIGN(window) \
   (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD &&   \
    GDK_WINDOW_TYPE (window) != GDK_WINDOW_OFFSCREEN)
@@ -431,6 +439,8 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
   wl_surface_commit (impl->surface);
   if (_gdk_wayland_is_shm_surface (impl->cairo_surface))
     _gdk_wayland_shm_surface_set_busy (impl->cairo_surface);
+
+  g_signal_emit (impl, signals[COMMITTED], 0);
 }
 
 static void
@@ -859,6 +869,19 @@ static const struct wl_surface_listener surface_listener = {
   surface_leave
 };
 
+static void
+on_parent_surface_committed (GdkWindowImplWayland *parent_impl,
+                             GdkWindow            *window)
+{
+  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
+
+  g_signal_handlers_disconnect_by_func (parent_impl,
+                                        (gpointer) on_parent_surface_committed,
+                                        window);
+
+  wl_subsurface_set_desync (impl->subsurface);
+}
+
 static void
 gdk_wayland_window_create_subsurface (GdkWindow *window)
 {
@@ -886,7 +909,14 @@ gdk_wayland_window_create_subsurface (GdkWindow *window)
         wl_subcompositor_get_subsurface (display_wayland->subcompositor,
                                          impl->surface, parent_impl->surface);
       wl_subsurface_set_position (impl->subsurface, window->x, window->y);
-      wl_subsurface_set_desync (impl->subsurface);
+
+      /* In order to synchronize the initial position with the initial frame
+       * content, wait with making the subsurface desynchronized until after
+       * the parent was committed.
+       */
+      g_signal_connect_object (parent_impl, "committed",
+                               G_CALLBACK (on_parent_surface_committed),
+                               window, 0);
       gdk_window_request_transient_parent_commit (window);
     }
 }
@@ -1294,6 +1324,23 @@ gdk_wayland_window_show (GdkWindow *window,
     gdk_wayland_window_attach_image (window);
 }
 
+static void
+unmap_subsurface (GdkWindow *window)
+{
+  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
+  GdkWindowImplWayland *parent_impl;
+
+  g_return_if_fail (impl->subsurface);
+  g_return_if_fail (impl->transient_for);
+
+  parent_impl = GDK_WINDOW_IMPL_WAYLAND (impl->transient_for->impl);
+  wl_subsurface_destroy (impl->subsurface);
+  g_signal_handlers_disconnect_by_func (parent_impl,
+                                        (gpointer) on_parent_surface_committed,
+                                        window);
+  impl->subsurface = NULL;
+}
+
 static void
 gdk_wayland_window_hide_surface (GdkWindow *window)
 {
@@ -1338,10 +1385,7 @@ gdk_wayland_window_hide_surface (GdkWindow *window)
         }
 
       if (impl->subsurface)
-        {
-          wl_subsurface_destroy (impl->subsurface);
-          impl->subsurface = NULL;
-        }
+        unmap_subsurface (window);
 
       if (impl->awaiting_frame)
         {
@@ -1793,24 +1837,18 @@ static void
 gdk_wayland_window_set_transient_for (GdkWindow *window,
                                       GdkWindow *parent)
 {
-  GdkWindowImplWayland *impl;
+  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
+
+  if (impl->subsurface)
+    unmap_subsurface (window);
 
-  impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
   impl->transient_for = parent;
 
   gdk_wayland_window_sync_parent (window);
 
-  if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_SUBSURFACE)
-    {
-      if (impl->subsurface)
-        {
-          wl_subsurface_destroy (impl->subsurface);
-          impl->subsurface = NULL;
-        }
-
-      if (parent && gdk_window_is_visible (window))
-        gdk_wayland_window_create_subsurface (window);
-    }
+  if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_SUBSURFACE &&
+      parent && gdk_window_is_visible (window))
+    gdk_wayland_window_create_subsurface (window);
 }
 
 static void
@@ -2392,6 +2430,13 @@ _gdk_window_impl_wayland_class_init (GdkWindowImplWaylandClass *klass)
   impl_class->show_window_menu = gdk_wayland_window_show_window_menu;
   impl_class->create_gl_context = gdk_wayland_window_create_gl_context;
   impl_class->invalidate_for_new_frame = gdk_wayland_window_invalidate_for_new_frame;
+
+  signals[COMMITTED] = g_signal_new ("committed",
+                                     G_TYPE_FROM_CLASS (object_class),
+                                     G_SIGNAL_RUN_LAST,
+                                     0,
+                                     NULL, NULL, NULL,
+                                     G_TYPE_NONE, 0);
 }
 
 void